namespace BingMaps.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Device.Location;
    using System.Reactive.Linq;
    using System.Windows;
    using GalaSoft.MvvmLight.Command;
    using GalaSoft.MvvmLight.Messaging;
    using Microsoft.Phone.Controls.Maps;
    using WP7Contrib.Logging;
    using WP7Contrib.Services.BingMaps;
    using WP7Contrib.Services.BingMaps.Model;
    using WP7Contrib.Services.Location;
    using WP7Contrib.Services.Navigation;
    using WP7Contrib.Services.Storage;

    public class ImageryViewModel : ViewModelBaseWp7
    {
        #region Constants and Fields

        private readonly IBingMapsService bingMapsService;

        private readonly ILocationService locationService;

        private IDisposable currentLocationObserver;

        private ImagerySearchCriterion imagerySearchCriterion;

        private Uri staticMapUrl;

        private RelayCommand selectCenterPointCommand;

        private RelayCommand selectCenterPointWithRouteCommand;

        private RelayCommand selectSpecificAreaWithRouteCommand;

        private RelayCommand selectSpecificAreaCommand;

        private RelayCommand selectQueryCommand;

        private RelayCommand selectRouteCommand;

        private string currentlySelectedImagerySet;

        private ImagerySet imagerySetInUse;

        private Optimize? optimizeRouteOptionInUse;

        private string currentlySelectedOptimize;

        private Avoid avoidOptionInUse;

        private string currentlySelectedAvoid;

        private int currentlySelectedZoomLevel;

        private string currentlySelectedModeOfTravel;

        private ModeOfTravel modeOfTravelOptionInUse;

        #endregion

        #region Constructors and Destructors

        public ImageryViewModel(
            INavigationService navigationService,
            IMessenger messenger,
            ILocationService locationService,
            IBingMapsService bingMapsService,
            ILog log)
            : base(navigationService, messenger, log)
        {
            this.locationService = locationService;
            this.bingMapsService = bingMapsService;

            this.ImagerySet = new List<string> { "Aerial imagery", "Aerial imagery with a road overlay", "Roads without imagery" };
            this.OptimizeRoute = new List<string> { "", "Shortest Route", "Quickest Route", "Quickest Route considering Traffic" };
            this.Avoid = new List<string> { "", "Avoid Highways", "Avoid Tolls", "Minimize Highways", "Minimize Tolls" };
            this.ModeOfTravel = new List<string> { "Driving", "Walking" };

            this.imagerySetInUse = WP7Contrib.Services.BingMaps.Model.ImagerySet.Aerial;
            this.avoidOptionInUse = new Avoid();
            this.modeOfTravelOptionInUse = WP7Contrib.Services.BingMaps.Model.ModeOfTravel.Driving;
            this.currentlySelectedZoomLevel = 5;
        }

        #endregion

        #region Properties

        public RelayCommand SelectCenterPointCommand
        {
            get
            {
                return this.selectCenterPointCommand ??
                       (this.selectCenterPointCommand =
                        new RelayCommand(this.ExecuteSelectCenterPointCommand));
            }
        }

        public RelayCommand SelectCenterPointWithRouteCommand
        {
            get
            {
                return this.selectCenterPointWithRouteCommand ??
                       (this.selectCenterPointWithRouteCommand =
                        new RelayCommand(this.ExecuteSelectCenterPointWithRouteCommand));
            }
        }

        public RelayCommand SelectSpecificAreaWithRouteCommand
        {
            get
            {
                return this.selectSpecificAreaWithRouteCommand ??
                       (this.selectSpecificAreaWithRouteCommand =
                        new RelayCommand(this.ExecuteSelectSpecificAreaWithRouteCommand));
            }
        }

        public RelayCommand SelectSpecificAreaCommand
        {
            get
            {
                return this.selectSpecificAreaCommand ??
                       (this.selectSpecificAreaCommand =
                        new RelayCommand(this.ExecuteSelectSpecificAreaCommand));
            }
        }

        public RelayCommand SelectRouteCommand
        {
            get
            {
                return this.selectRouteCommand ??
                       (this.selectRouteCommand =
                        new RelayCommand(this.ExecuteSelectRouteCommand));
            }
        }

        public RelayCommand SelectQueryCommand
        {
            get
            {
                return this.selectQueryCommand ??
                       (this.selectQueryCommand =
                        new RelayCommand(this.ExecuteSelectQueryCommand));
            }
        }

        public ImagerySearchCriterion ImagerySearchCriterion
        {
            get
            {
                return this.imagerySearchCriterion;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.imagerySearchCriterion, value, "ImagerySearchCriterion");
            }
        }

        public Uri StaticMap
        {
            get
            {
                return this.staticMapUrl;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.staticMapUrl, value, "StaticMap");
            }
        }

        public List<string> ImagerySet { get; private set; }

        public List<string> OptimizeRoute { get; private set; }

        public List<string> Avoid { get; set; }

        public List<string> ModeOfTravel { get; set; }


        public int CurrentlySelectedZoomLevel
        {
            get
            {
                return this.currentlySelectedZoomLevel;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.currentlySelectedZoomLevel, value, "CurrentlySelectedZoomLevel");
            }
        }

        public string CurrentlySelectedImagerySet
        {
            get
            {
                return this.currentlySelectedImagerySet;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.currentlySelectedImagerySet, value, "CurrentlySelectedImagerySet");
                switch (this.currentlySelectedImagerySet)
                {
                    case "Aerial imagery":
                        {
                            this.imagerySetInUse = WP7Contrib.Services.BingMaps.Model.ImagerySet.Aerial;
                            return;
                        }
                    case "Aerial imagery with a road overlay":
                        {
                            this.imagerySetInUse = WP7Contrib.Services.BingMaps.Model.ImagerySet.AerialWithLabels;
                            return;
                        }
                    case "Roads without imagery":
                        {
                            this.imagerySetInUse = WP7Contrib.Services.BingMaps.Model.ImagerySet.Road;
                            return;
                        }
                }
            }
        }

        public string CurrentlySelectedOptimize
        {
            get
            {
                return this.currentlySelectedOptimize;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.currentlySelectedOptimize, value, "CurrentlySelectedOptimize");
                switch (this.currentlySelectedOptimize)
                {
                    case "":
                        {
                            this.optimizeRouteOptionInUse = null;
                            return;
                        }
                    case "Shortest Route":
                        {
                            this.optimizeRouteOptionInUse = Optimize.Distance;
                            return;
                        }
                    case "Quickest Route":
                        {
                            this.optimizeRouteOptionInUse = Optimize.Time;
                            return;
                        }
                    case "Quickest Route considering Traffic":
                        {
                            this.optimizeRouteOptionInUse = Optimize.TimeWithTraffic;
                            return;
                        }
                }
            }
        }

        public string CurrentlySelectedAvoid
        {
            get
            {
                return this.currentlySelectedAvoid;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.currentlySelectedAvoid, value, "CurrentlySelectedAvoid");
                switch (this.currentlySelectedOptimize)
                {
                    case "":
                        {
                            this.avoidOptionInUse = new Avoid();
                            return;
                        }

                    case "Avoid Highways":
                        {
                            this.avoidOptionInUse = new Avoid { Highways = true };
                            return;
                        }
                    case "Avoid Tolls":
                        {
                            this.avoidOptionInUse = new Avoid { Tolls = true };
                            return;
                        }
                    case "Minimize Highways":
                        {
                            this.avoidOptionInUse = new Avoid { MinimizeHighways = true };
                            return;
                        }
                    case "Minimize Tolls":
                        {
                            this.avoidOptionInUse = new Avoid { MinimizeTolls = true };
                            return;
                        }
                }
            }
        }

        public string CurrentlySelectedModeOfTravel
        {
            get
            {
                return this.currentlySelectedModeOfTravel;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.currentlySelectedModeOfTravel, value, "CurrentlySelectedModeOfTravel");
                switch (this.currentlySelectedModeOfTravel)
                {
                    case "Driving":
                        {
                            this.modeOfTravelOptionInUse = WP7Contrib.Services.BingMaps.Model.ModeOfTravel.Driving;
                            return;
                        }
                    case "Walking":
                        {
                            this.modeOfTravelOptionInUse = WP7Contrib.Services.BingMaps.Model.ModeOfTravel.Walking;
                            return;
                        }
                }
            }
        }

        #endregion

        #region Methods

        protected override void IsBeingActivated(IStorage storage)
        {
        }

        protected override void IsBeingDeactivated(IStorage storage)
        {
        }

        public override void Cleanup()
        {
            if (this.currentLocationObserver != null)
            {
                this.currentLocationObserver.Dispose();
                this.currentLocationObserver = null;
            }

            base.Cleanup();
        }

        private void ExecuteSelectCenterPointCommand()
        {
            this.currentLocationObserver = this.locationService
                .Location()
                .ObserveOnDispatcher()
                .Subscribe(geoCoordinate =>
                {
                    var criterion = CriterionFactory.CreateImageryUrlForCenterPoint(this.imagerySetInUse, geoCoordinate, this.CurrentlySelectedZoomLevel, new Size(456, 535));
                    this.StaticMap = this.bingMapsService.ImageryUrlForCenterPoint(criterion);
                });
        }

        private void ExecuteSelectCenterPointWithRouteCommand()
        {
            var start = new GeoCoordinate(40.77, -73.98);
            var end = new GeoCoordinate(41.51, -87.39);

            var wayPoints = new List<WayPoint> { new WayPoint { Point = start }, new WayPoint { Point = end } };

            var criterion = CriterionFactory.CreateImageryUrlForCentrePointWithRoute(this.imagerySetInUse,this.modeOfTravelOptionInUse, start, this.CurrentlySelectedZoomLevel, wayPoints, new Size(456, 535), this.avoidOptionInUse);
            this.StaticMap = this.bingMapsService.ImageryUrlForCenterPointWithRoute(criterion);
        }

        private void ExecuteSelectSpecificAreaWithRouteCommand()
        {
            const double south = 37.317227;
            const double west = -122.318439;
            const double north = 37.939081;
            const double east = -122.194565;

            var start = new GeoCoordinate(south, west);
            var end = new GeoCoordinate(north, east);

            var wayPoints = new[] { new WayPoint { Point = start }, new WayPoint { Point = end } };

            var criterion = CriterionFactory.CreateImageryUrlForSpecificAreaWithRoute(this.imagerySetInUse, this.modeOfTravelOptionInUse, wayPoints, new LocationRect(north, west, south, east));
            this.StaticMap = this.bingMapsService.ImageryUrlForSpecificAreaWithRoute(criterion);
        }

        private void ExecuteSelectSpecificAreaCommand()
        {
            const double south = 37.317227;
            const double west = -122.318439;
            const double north = 37.939081;
            const double east = -122.194565;

            var criterion = CriterionFactory.CreateImageryUrlForSpecificArea(this.imagerySetInUse, new LocationRect(north, west, south, east));
            this.StaticMap = this.bingMapsService.ImageryUrlForSpecificArea(criterion);
        }

        private void ExecuteSelectRouteCommand()
        {
            var start = new GeoCoordinate(40.77, -73.98);
            var end = new GeoCoordinate(41.51, -87.39);

            var wayPoints = new[] { new WayPoint { Point = start }, new WayPoint { Point = end } };

            var pushPins = new List<PushPin>();

            var criterion = CriterionFactory.CreateImageryUrlForRoute(this.imagerySetInUse, this.modeOfTravelOptionInUse,  wayPoints, DateTime.Now, this.optimizeRouteOptionInUse, pushPins, TimeType.Departure);
            this.StaticMap = this.bingMapsService.ImageryUrlForRoute(criterion);
        }

        private void ExecuteSelectQueryCommand()
        {
            var criterion = CriterionFactory.CreateImageryUrlForQuery(this.imagerySetInUse, "space needle,seattle");
            this.StaticMap = this.bingMapsService.ImageryUrlForQuery(criterion);
        }

        #endregion
    }
}

